#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

/* 半委任式的ffwd */
typedef struct _sffwdnode{
	struct{
		APDSFUTURE_DECL_NOFLAG
		word_t flag;
	} CACHE_ALIGNED;
} _sffwdnode_t;

#define SFFWD_REQS_PERGROUPS	(16)	

#define SFFWD_MAX_GROUPS 	(APDS_MAX_THREADS/SFFWD_REQS_PERGROUPS)
#define SFFWD_GROUPID(tid)	(tid / (SFFWD_REQS_PERGROUPS - 1))
#define SFFWD_GROUP_TID(tid)	(tid % (SFFWD_REQS_PERGROUPS - 1))

typedef struct _sffwd {
	/* SFFWD_REQS_PERGROUPS - 1 是flag */
	word_t server_responses[SFFWD_MAX_GROUPS][SFFWD_REQS_PERGROUPS];
	_sffwdnode_t node[SFFWD_MAX_GROUPS*(SFFWD_REQS_PERGROUPS - 1)];
	lkword_t lock;
} PAGE_ALIGNED _sffwd_t;


DECL_COMBINE(sffwd)


SAI void __sffwd_init(_sffwd_t *s)
{
	// s->lock = 0;
	// s->cur_helpedgroup = 0;
	__memset(s, 0, sizeof(_sffwd_t));
}
SAI void *__sffwd_destruct(_sffwd_t *s){
	return (void *)s;
}

/* 提交请求 通知远程, 完成请求通知线程 */
SAI _sffwdnode_t *__sffwd_fetch(_sffwd_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo){
	tid_t tid = tinfo->tid;
	_sffwdnode_t *node = &s->node[tid];
	word_t local_flag = node->flag ^ BIT(SFFWD_GROUP_TID(tid)); 
	/* 参数入队 */
	apds_movargs(&node->args, args);
	/* 通知 */
	node->func = pfunc;
	/* 写入flag */
	atm_strel(&node->flag, local_flag);

	return node;
}

SAI apdsret_t __sffwd_wait(_sffwd_t *s, _sffwdnode_t *node, INFUN(cmbn_wait, waitpolicy), apdstinfo_t *tinfo){
	waitcnt_t cnt = -1;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;
	word_t pending;
	word_t local_flag = node->flag;
	tid_t tid = tinfo->tid;
	tid_t ingroup_offst = SFFWD_GROUP_TID(tid);
	word_t mask = BIT(ingroup_offst);
	word_t group_id = SFFWD_GROUPID(tid);
	lock = atm_ld(&s->lock);
	do {
		pending = (atm_ld(&s->server_responses[group_id][SFFWD_REQS_PERGROUPS - 1]) ^ local_flag) & mask;
		while (lock && pending)
		{
			cnt = waitpolicy->waitf(&s->lock, &locked, cnt);
			lock = atm_ld(&s->lock);
			pending = (atm_ld(&s->server_responses[group_id][SFFWD_REQS_PERGROUPS - 1]) ^ local_flag) & mask;
		}
		if (!pending){
			mp_rmb();
			return atm_ld(&s->server_responses[group_id][ingroup_offst]);
		}
		if(!lock){
			if (cas_acq_s(&s->lock, &lock, 1))
				break;
		}
	} while(TRUE);

	/* 最后一个是bits */
	word_t cached_response[SFFWD_REQS_PERGROUPS];
	
	extern cid_t __max_threads;
	tid_t max_tid = atm_ld(&__max_threads);
	word_t helped_group_nrs = SFFWD_GROUPID(max_tid) + 1; 
	
	_sffwdnode_t *cur;
	tid_t cur_group_tid = 0;
	tid_t cur_tid = 0;
	word_t help_cnt = 0;
	word_t ori_flag;
	#pragma unroll SFFWD_MAX_GROUPS
	for (word_t gidx = 0; gidx < helped_group_nrs; gidx++){
		/* 每组帮助 */
		/* 获得上一次的flag */
		// cached_response[SFFWD_REQS_PERGROUPS - 1] = s->server_responses[gidx][SFFWD_REQS_PERGROUPS - 1];
		__builtin_memcpy(&cached_response[0], &s->server_responses[gidx][0], sizeof(cached_response));
		ori_flag = cached_response[SFFWD_REQS_PERGROUPS - 1];

		#pragma unroll SFFWD_REQS_PERGROUPS
		for (tid_t tidx = 0; tidx < SFFWD_REQS_PERGROUPS - 1; tidx++){
			/* 组内帮助 */
			cur_tid = cur_group_tid + tidx;
			cur = &s->node[cur_tid];
			/* 相反说明需要帮助 */
			if ((atm_ld(&cur->flag) ^ ori_flag) & BIT(tidx))
			{
				cached_response[tidx] = apds_sync_single(cur->func, &cur->args);
				/* 翻转位 */
				cached_response[SFFWD_REQS_PERGROUPS - 1] ^= BIT(tidx);
				help_cnt++;
			}
			/* 如果不拷贝原有结果 */
			/* else{
				cached_response[tidx] = s->server_responses[gidx][tidx];
			} */
		}
		/* 增加 */
		cur_group_tid +=  SFFWD_REQS_PERGROUPS - 1;
		
		if (ori_flag ^ cached_response[SFFWD_REQS_PERGROUPS - 1]) {
			/* 写回 */
		#ifndef HDEF_WEAK_MEMORY_ORDER
			__builtin_memcpy(&s->server_responses[gidx][0], &cached_response[0], sizeof(cached_response));
		#else
			#pragma unroll SFFWD_REQS_PERGROUPS
			for(tid_t tidx = 0; tidx < SFFWD_REQS_PERGROUPS - 1; tidx++){
				atm_st(&s->server_responses[gidx][tidx], cached_response[tidx]);
			}
			/* 结果写回之前确保有一次memory barrier */
			atm_strel(&s->server_responses[gidx][SFFWD_REQS_PERGROUPS - 1], cached_response[SFFWD_REQS_PERGROUPS - 1]);
		#endif
		}
	}
	
	/* 释放锁 */
	atm_strel(&s->lock, 0);
	
	waitpolicy->wakef(&s->lock, &unlocked);
	return atm_ld(&s->server_responses[group_id][ingroup_offst]);
}

PARAM_NONULL(1) SAI _sffwd_t *_sffwd_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_sffwd_t)) return NULL;
	_sffwd_t *f = (_sffwd_t *)ptr;
	__sffwd_init(f);
	return f;
}
PARAM_NONULL(1) SAI void *_sffwd_destruct(_sffwd_t *f, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __sffwd_destruct(f);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _sffwdnode_t *_sffwd_fetch(_sffwd_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	return __sffwd_fetch(f, pfunc, args, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _sffwd_wait(_sffwd_t *f, _sffwdnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	apdsret_t ret = __sffwd_wait(f, node, waitpolicy, tinfo);
	return ret;
}
